package com.coin900.coin.base;


import com.coin900.coin.base.config.ValidationMessage;
import com.coin900.coin.base.enums.ResponseEnum;
import com.coin900.coin.persist.FuVO;
import com.value.exception.AppException;
import com.value.mybatis.Pagination;
import com.value.utils.DateUtil;
import com.value.utils.RegExpUtil;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.*;

/**
 * Unibon
 * <p/>
 * Copyright (c) 2012 YouPeng ValueSoft Inc., All rights reserved.
 * <p/>
 * This software is the confidential and proprietary information of
 * ValueSoft Company. ("Confidential Information").  You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with ValueSoft.com.
 * <p/>
 * Revision History
 * Date      		Programmer       Notes
 * 2014/5/13   	     libo		     Initial
 * *********************************************************************
 */
public class AjaxResponse {
    private ModelAndView mav = new ModelAndView();
    private HttpServletRequest request;
    private String jsonPath;
    private List<Map<String, Object>> errors = new ArrayList<>();
    private String dateFormat = "yyyy-MM-dd HH:mm:ss";
    private boolean escape = true;
    private HashSet filterEscapeField = new HashSet() {
        {

        }
    };


    public AjaxResponse(HttpServletRequest request,
                        String jsonPath) {
        mav.addObject("stat", ResponseEnum.OK);
        mav.addObject("data", new JSONObject());
        mav.addObject("errors", errors);
        this.request = request;
        this.jsonPath = jsonPath;
    }

    /**
     * 添加集合
     *
     * @param coll
     */
    public void addCollection(Object coll) {
        JSONObject data = getData();
        data.put("collection", coll);
    }

    public ModelMap getModelMap() {
        return this.mav.getModelMap();
    }

    /**
     * 添加模型
     *
     * @param model
     */
    public void addModel(Object model) {
        if (model == null) {
            return;
        }
        JSONObject data = getData();
        JSONObject modelStru = getModelStru();
        if (modelStru == null) {
            data.put("model", model);
        } else {
            JSONObject jsonObject = filterModel(model, modelStru);
            data.put("model", jsonObject);
        }
    }

    public void addModels(List model) {
        JSONObject modelStru = getModelStru();
        if (modelStru == null) {
            addCollection(model);
        } else {
            JSONArray array = new JSONArray();
            if (model != null) {
                for (Object result : model) {
                    JSONObject jsonObject = filterModel(result, modelStru);
                    array.add(jsonObject);
                }
            }
            addCollection(array);
        }
    }

    public void addObject(String key, Object value) {
        JSONObject data = getData();
        if (value == null) {
            data.put(key, "");
        } else if (FuVO.class.isAssignableFrom(value.getClass())) {
            JSONObject modelStru = getModelStru();
            if (modelStru != null) {
                JSONObject jsonObject = filterModel(value, modelStru);
                data.put(key, jsonObject);
            }
        } else if (value instanceof List) {
            JSONObject modelStru = getModelStru();
            JSONObject struct = (JSONObject) modelStru.get(key);
            JSONArray array = new JSONArray();
            List list = (List) value;
            for (Object result : list) {
                JSONObject jsonObject = filterModel(result, struct);
                array.add(jsonObject);
            }
            data.put(key, array);

        } else {
            data.put(key, value);
        }
    }

    public void setStat(ResponseEnum respEnum) {
        mav.addObject("stat", respEnum);
    }

    /**
     * 添加分页信息
     *
     * @param page
     */
    public void addPagination(Pagination page) {
        JSONObject data = getData();
        data.put("currentPage", page.getCurrentPage());
        data.put("totalPages", page.getTotalPages());
        data.put("totalCount", page.getCount());

        List<?> results = page.getResults();
        JSONObject modelStru = getModelStru();
        if (modelStru == null) {
            addCollection(results);
        } else {
            JSONArray array = new JSONArray();
            if (results != null) {
                for (Object result : results) {
                    JSONObject jsonObject = filterModel(result, modelStru);
                    array.add(jsonObject);
                }
            }
            addCollection(array);
        }
    }

    /**
     * 添加错误信息
     *
     * @param msg
     */
    public void addError(String msg) {
        mav.addObject("stat", ResponseEnum.ERROR);
        Map<String, Object> error = new HashMap<>();
        error.put("field", "error");
        error.put("msg", msg);
        errors.add(error);
    }

    public void addError(BindingResult br) {
        mav.addObject("stat", ResponseEnum.VERIFY_FAIL);
        for (ObjectError objectError : br.getAllErrors()) {
            Map<String, Object> error = new HashMap<>();
            error.put("field", ((FieldError) objectError).getField());
            error.put("msg", objectError.getDefaultMessage());
            errors.add(error);
        }
    }

    /**
     * 验证错误
     *
     * @param field
     * @param failCode
     * @return
     */
    public void addError(String field, String failCode) {
        mav.addObject("stat", ResponseEnum.VERIFY_FAIL);
        Map<String, Object> error = new HashMap<>();
        error.put("field", field);
        String message = ValidationMessage.getMessage(failCode);
        if (message == null) {
            error.put("msg", failCode);
        } else {
            error.put("msg", ValidationMessage.getMessage(failCode));
        }
        errors.add(error);
    }

    public JSONObject getData() {
        return (JSONObject) mav.getModelMap().get("data");
    }

    public ModelAndView getMav() {
        return mav;
    }

    public static Map<String, JSONObject> modelCached = new HashMap<>();

    /**
     * 取得模型返回结构
     *
     * @return
     */
    private JSONObject getModelStru() {
        String url = buildUrl();
        JSONObject modelStru = null;
        try {
            modelStru = modelCached.get(url);
            if (modelStru == null || "dev".equals(Constants.env)) {
                InputStreamReader inputStreamReader = new InputStreamReader(this.getClass().getResourceAsStream(this.jsonPath), "UTF-8");
                String jsonTxt = org.apache.commons.io.IOUtils.toString(inputStreamReader);
                JSONObject obj = JSONObject.fromObject(jsonTxt);
                modelStru = (JSONObject) obj.get(url);
                if (modelStru != null) {
                    modelCached.put(url, modelStru);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("错误的json路径"+url);
        }
        return modelStru;
    }

    private JSONObject filterModel(Object model, JSONObject modelStru) {
        JSONObject jsonObject = new JSONObject();
        Iterator<Map.Entry<String, JSONObject>> iterator = modelStru.entrySet().iterator();
        Map<String, Field> fieldMap = getField(model.getClass());
        while (iterator.hasNext()) {
            Map.Entry<String, JSONObject> next = iterator.next();
            String key = next.getKey();
            Object objValue = next.getValue();
            try {
                if (model instanceof Map) {
                    if (((Map) model).containsKey(key)) {
                        Object mapObj = ((Map) model).get(key);
                        if (mapObj == null) {
                            jsonObject.put(key, "");
                        } else {
                            Class typeClass = mapObj.getClass();
                            if (List.class.isAssignableFrom(mapObj.getClass())) {
                                List list = (List) mapObj;
                                JSONObject value = next.getValue();
                                JSONArray array = new JSONArray();
                                for (Object obj : list) {
                                    if (obj instanceof String) {
                                        array.add(obj);
                                    } else {
                                        JSONObject jsonObj = filterModel(obj, value);
                                        array.add(jsonObj);
                                    }
                                }
                                jsonObject.put(key, array);
                            } else if (Date.class.isAssignableFrom(mapObj.getClass())) {
                                Date date = (Date) mapObj;
                                if (objValue instanceof String) {
                                    jsonObject.put(key, DateUtil.dateToString(date, dateFormat));
                                } else if (objValue instanceof JSONObject) {
                                    JSONObject jsonObj = (JSONObject) objValue;
                                    String format = (String) jsonObj.get("format");
                                    jsonObject.put(key, DateUtil.dateToString(date, format));
                                }
                            } else if (Map.class.isAssignableFrom(mapObj.getClass())) {
                                jsonObject.put(key, JSONObject.fromObject(mapObj));
                            } else if (FuVO.class.isAssignableFrom(typeClass)) {
                                JSONObject value = next.getValue();
                                JSONObject jsonObj = filterModel(mapObj, value);
                                jsonObject.put(key, jsonObj);
                            } else {
                                if (String.class.isAssignableFrom(mapObj.getClass()) && escape && !filterEscapeField.contains(key)) {
                                    jsonObject.put(key, (mapObj + "").replace("<", "&lt;").replace(">", "&gt;"));
                                } else {
                                    jsonObject.put(key, mapObj);
                                }
                            }
                        }
                    }
                } else {
                    Field field = fieldMap.get(key);
                    if (field != null) {
                        field.setAccessible(true);
                        Class typeClass = field.getType();
                        if (typeClass.equals(Short.class)
                                || typeClass.equals(Boolean.class) || typeClass.equals(String.class)
                                || typeClass.equals(Double.class) || typeClass.equals(Integer.class)
                                || typeClass.equals(Long.class) || typeClass.equals(BigDecimal.class)) {
                            Object obj = field.get(model);
                            if (obj == null || "null".equals(obj)) {
                                jsonObject.put(key, "");
                            } else {
                                if (typeClass.equals(String.class) && escape && !filterEscapeField.contains(key)) {
                                    jsonObject.put(key, (obj + "").replace("<", "&lt;").replace(">", "&gt;"));
                                } else {
                                    jsonObject.put(key, obj);
                                }
                            }

                        } else if (typeClass.equals(Date.class)) {
                            Date date = (Date) field.get(model);
                            if (objValue instanceof String) {
                                jsonObject.put(key, DateUtil.dateToString(date, dateFormat));
                            } else if (objValue instanceof JSONObject) {
                                JSONObject jsonObj = (JSONObject) objValue;
                                String format = (String) jsonObj.get("format");
                                jsonObject.put(key, DateUtil.dateToString(date, format));
                            }
                        } else if (typeClass.equals(List.class)) {
                            JSONObject value = next.getValue();
                            List list = (List) field.get(model);
                            if (list == null) {
                                continue;
                            }
                            JSONArray array = new JSONArray();
                            for (Object obj : list) {
                                JSONObject jsonObj = filterModel(obj, value);
                                array.add(jsonObj);
                            }
                            jsonObject.put(key, array);
                        } else if (FuVO.class.isAssignableFrom(typeClass)) {
                            JSONObject value = next.getValue();
                            Object obj = field.get(model);
                            if (obj != null) {
                                JSONObject jsonObj = filterModel(obj, value);
                                jsonObject.put(key, jsonObj);
                            }
                        } else if (Map.class.isAssignableFrom(typeClass)) {
                            Object obj = field.get(model);
                            jsonObject.put(key, obj);
                        } else {
                            throw new AppException(key + " Type not defined");
                        }
                    }
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return jsonObject;
    }

    private Map<String, Field> getField(Class _class) {
        Map<String, Field> fieldMap = new HashMap<>();
        Field[] fields = _class.getDeclaredFields();
        for (Field field : fields) {
            fieldMap.put(field.getName(), field);
        }
        if (_class.getSuperclass() != null) {
            Map<String, Field> superFieldMap = getField(_class.getSuperclass());
            fieldMap.putAll(superFieldMap);
        }
        return fieldMap;
    }

    /**
     * 取得请求路径和方法
     *
     * @return
     */
    public String buildUrl() {
        String path = request.getRequestURI();
        String contextPath = request.getContextPath();
        path = path.replaceFirst(contextPath, "");
        StringBuffer sb = new StringBuffer();

        //过滤请求后缀如.json .xml等
        if (path.indexOf(".") != -1) {
            path = path.split("\\.")[0];
        }
        //过滤数字
        String[] pathArray = path.split("/");
        for (String p : pathArray) {
            if (!StringUtils.isBlank(p)) {
                sb.append("/");
                if (RegExpUtil.isNumeric(p)) {
                    sb.append("*");
                } else {
                    sb.append(p);
                }
            }
        }
        String method = request.getMethod();
        String _method = request.getParameter("_method");
        sb.append(":");
        if (!StringUtils.isBlank(_method)) {
            sb.append(_method.toLowerCase());
        } else {
            sb.append(method.toLowerCase());
        }
        return sb.toString();
    }

    public AjaxResponse addFilterEscapeField(String field) {
        filterEscapeField.add(field);
        return this;
    }

    public AjaxResponse clearFilterEscapeField() {
        filterEscapeField.clear();
        return this;
    }

    public boolean isEscape() {
        return escape;
    }

    public void setEscape(boolean escape) {
        this.escape = escape;
    }

    public String getDateFormat() {
        return dateFormat;
    }

    public void setDateFormat(String dateFormat) {
        this.dateFormat = dateFormat;
    }
}
